﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using Allegro.Framework;

namespace Allegro.Mathlib
{
    public class Resultset : IResultset
    {
        int _nDataPoints;
        int _nDim;
        double[] _x;
        List<double[]> _y;
        int _currentIndex;
        private List<VariableBinding> _variableBindings;

        public Resultset(int nDataPoints, int nDim)
        {
            _currentIndex = 0;
            _nDataPoints = nDataPoints;
            _nDim = nDim;
            _x = new double[_nDataPoints];
            _y = new List<double[]>(_nDim);
            for (int i = 0; i < _nDim; i++)
            {
                _y.Add(new double[_nDataPoints]);
            }
        }

        /// <summary>
        /// Add Data point
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void AddData(double x, double[] y)
        {           
            int l = y.GetLength(0);
            if (l != _nDim)
                ErrorHandler.Error("Resultset error: Result vector has wrong dimension");
            if(_currentIndex >= _x.GetLength(0))
                ErrorHandler.Error("Resultset error: Out of bounds - unable to addd more data");
            _x[_currentIndex] = x;
            for (int i = 0; i < _nDim; i++)
            {
                _y[i][_currentIndex] = y[i];
            }
            _currentIndex++;
        }

        public int Length
        {
            get { return _currentIndex; }
        }

        public int Dimension
        {
            get { return _nDim; }
        }

        /// <summary>
        /// Array of x coordinates
        /// </summary>
        public double[] x
        {
            get
            {
                return _x;
            }
        }
        /// <summary>
        /// Array of y vector
        /// </summary>
        public double[][] y
        {
            get
            {
                return _y.ToArray();
            }
        }

        /// <summary>
        /// Variable bindings
        /// </summary>
        public List<VariableBinding> VariableBindings
        {
            get { return _variableBindings; }
            set{ _variableBindings = value;}
        }

        /// <summary>
        /// Output the result set to a tabbed-delited text file
        /// </summary>
        /// <param name="writer"></param>
        public void Write(TextWriter writer)
        {
            Write(writer, "\t");
        }

        /// <summary>
        /// Output the results. A log interval dy is specified, and the resultset will be logged with an interval dy 
        /// of the output y[i], where i = logColumn. 
        /// If logarithmicInterval = true,
        /// dy is a logarithmic interval, i.e. an interval in Log(10,y[i])
        /// </summary>
        /// <param name="writer">Writer to output to</param>
        /// <param name="dy">Log interval</param>
        /// <param name="logColumn">Index column</param>
        /// <param name="logarithmicInterval">True if the log interval is logarithmic (base 10)</param>
        public void Write(TextWriter writer, double dy, int logColumn, bool logarithmicInterval)
        {
            int l = _x.GetLength(0);
            if (l == 0)
                return;
            int nData = Length;
            int nDim = _y.Count;
            double yLog = _y[logColumn][0];
            bool logNow = true;
            for (int i = 0; i < nData; i++)
            {
                if (i == 0 || i == nData - 1
                    || (!logarithmicInterval && _y[logColumn][i] >= yLog + dy)
                    || (logarithmicInterval && Math.Log10(_y[logColumn][i]) > yLog + dy)
                    )
                {
                    logNow = true;
                }
                else
                {
                    logNow = false;
                }
                if (logNow)
                {
                    yLog = logarithmicInterval ? System.Math.Log10(_y[logColumn][i]) : _y[logColumn][i];
                    writer.Write("{0,-23:G}", x[i].ToString("G", CultureInfo.InvariantCulture));
                    for (int j = 0; j < nDim; j++)
                    {
                        writer.Write("{0,-23:G}", _y[j][i].ToString("G", CultureInfo.InvariantCulture));
                    }
                    writer.Write(Environment.NewLine);
                }
            }
        }

        /// <summary>
        /// Write results to the datafile with minimum time increment
        /// </summary>
        /// <param name="dataFile"></param>
        /// <param name="minTimeStep"></param>
        public void WriteResults(TextWriter writer, double dtMin)
        {
            if (_x.GetLength(0) == 0)
                return;
            double tLog = 0;
            int nData = Length;
            int nDim = _y.Count;
            for (int i = 0; i < nData; i++)
            {
                if (i == 0 || _y[0][i] >= tLog + dtMin)
                {
                    tLog = _y[0][i];
                    writer.Write("{0,-23:G}", x[i].ToString("G", CultureInfo.InvariantCulture));
                    for (int j = 0; j < nDim; j++)
                    {
                        writer.Write("{0,-23:G}", _y[j][i].ToString("G", CultureInfo.InvariantCulture));
                    }
                    writer.Write(Environment.NewLine);
                }
            }
        }

        /// <summary>
        /// Output the result set to a file with specified delimiter
        /// </summary>
        /// <param name="writer"></param>
        /// <param name="delim"></param>
        public void Write(TextWriter writer, string delim)
        {
            if (_x.GetLength(0) == 0)
                return;
            int nData = Length;
            int nDim = _y.Count;
            for (int i = 0; i < nData; i++)
            {
                writer.Write("{0,-23:G}", x[i].ToString("G", CultureInfo.InvariantCulture));
                for (int j = 0; j < nDim; j++)
                {
                    writer.Write("{0,-23:G}", _y[j][i].ToString("G", CultureInfo.InvariantCulture));
                }
                writer.Write(Environment.NewLine);
            }
        }
    }
}
